using System;
using System.Collections.Generic;
using System.Threading;
using System.Collections;
using System.Text;

namespace OliveChess
{
    using System.Runtime.InteropServices;
    using System.Diagnostics;

    class Engine
    {
        private readonly Thread myThread;
        private bool debugMode;
        
        private const int MIN_SCORE = -30000;
        private const int MAX_SCORE = 30000;
        private Position currentPosition;
        Stopwatch watch;

        private int nodeMaxNumber;

        private BrainMode brainMode;
        private BrainStatus brainStatus;
        private int depthLevel;
        private long thinkDelay;
        private Queue<Node> nodeQueue; // Nodes for every
        private List<Node> possibleNodes; // Nodes for possible moves
        //private List<Move> possibleMoves; // possible moves

        //private bool IsSearchmoveMode;
        private TimeSpan wtime; //total time left for white in millisec
        private TimeSpan btime; //total time left for black in millisec
        private TimeSpan winc;
        private TimeSpan binc;

        private bool isVerbose;

        private bool isSmartTiming;

        private int maxDepth;

        // Hashtable
        //private Hashtable hashtable;
        //private Dictionary<String, ArrayBoardNode> hashtable; // safer type but be carefull with different threads access
        //private int hashMaxSize = 800000;

        // Coefficient of each piece in centipawn
        // {WP,W?,W?,WR,WQ,WK,BP,B?,B?,BR,BQ,BK}
        private static int[] coefficient = { 85, 325, 325, 500, 975, 10000000, -85, -325, -325, -500, -975, -10000000 };

        private void SetPawnValue(int value)
        {
            coefficient[0] = value;
            coefficient[6] = -value;
        }
        private void SetKnightValue(int value)
        {
            coefficient[1] = value;
            coefficient[7] = -value;
        }
        private void SetBishopValue(int value)
        {
            coefficient[2] = value;
            coefficient[8] = -value;
        }
        private void SetRookValue(int value)
        {
            coefficient[3] = value;
            coefficient[9] = -value;
        }
        private void SetQueenValue(int value)
        {
            coefficient[4] = value;
            coefficient[10] = -value;
        }


        public Engine()
        {
            // Stopwatch (timer)
            watch = new Stopwatch();

            // Smart timing (default = true)
            isSmartTiming = false;

            // Hash
            //hashtable = new Hashtable();
            // TODO : manage capacity
            //hashtable = new Dictionary<string, ArrayBoardNode>(hashMaxSize); // test 0=no hash
            //hashMaxSize = GetHashMaxSize();

            myThread = new Thread(new ThreadStart(this.NodeInjectorThreadLoop));

            this.brainStatus = BrainStatus.PAUSED;
            possibleNodes = new List<Node>();
            nodeQueue = new Queue<Node>();
            maxDepth = -1;
            this.currentPosition = new Position();

            // TOD debug init = Blitz 2'+6"/ply - to be removed
            this.SetWtime(120000);
            this.SetWinc(6000);
            this.SetBtime(120000);
            this.SetBinc(6000);
            //this.SetMovetimeMode(6000);
            SetStartPosition();


            // Lancement du thread
            myThread.Start(); 
        }

        public void Quit()
        {
            myThread.Abort();
        }

        // Main thread - feeding nodes
        private void NodeInjectorThreadLoop()
        {
            // This threads will always run
            while (Thread.CurrentThread.IsAlive)
            {
                // Thread main task
                if (brainStatus == BrainStatus.THINKING)
                {
                    long nodeCount = 0;
                    watch.Start();
                    // reajust thinking time
                    if(brainMode == BrainMode.ADJUSTTIME_MODE) UpdateThinkDelay();
                    
                    while (brainStatus == BrainStatus.THINKING)
                    {
                        switch (brainMode)
                        {
                            case BrainMode.ADJUSTTIME_MODE:
                            case BrainMode.MOVETIME_MODE:
                                if (watch.ElapsedMilliseconds < thinkDelay)
                                {
                                    nodeCount += InjectNodes();
                                }
                                else
                                {
                                    brainStatus = BrainStatus.PAUSED;
                                }
                                break;
                            case BrainMode.MATE_MODE:
                            case BrainMode.DEPTH_MODE:
                                if (nodeQueue.Peek().GetDepth() <= depthLevel)
                                {
                                    nodeCount += InjectNodes();
                                }
                                else
                                {
                                    brainStatus = BrainStatus.PAUSED;
                                }
                                break;
                            case BrainMode.NODES_MODE:
                                if (nodeQueue.Count < this.nodeMaxNumber)
                                {
                                    nodeCount += InjectNodes();
                                }
                                else
                                {
                                    brainStatus = BrainStatus.PAUSED;
                                }
                                break;
                            case BrainMode.INFINITE_MODE:
                                nodeCount += InjectNodes();
                                break;
                            default:
                                brainStatus = BrainStatus.PAUSED;
                                break;
                        }
                    }
                    
                    //nodeCount = nodeQueue.Count;
                    if(isVerbose) System.Console.WriteLine("info nodes " + nodeCount);
                        
                    long nps = nodeCount/watch.ElapsedMilliseconds;
                    if(isVerbose) System.Console.WriteLine("info nps " + nps);
                    Move bestMove = this.GetBestMove();
                    if (bestMove != Move.Null)
                    {
                        //this.currentPosition.Play(bestMove);
                        // Display best move
                        Console.WriteLine("bestmove " + bestMove.ToString());
                    }
                    this.Stop();
                }
            }
        }

        private int InjectNodes()
        {
            if (nodeQueue.Count > 0)
            {
                Node currentNode = nodeQueue.Dequeue();
                if (currentNode != null && !currentNode.toBeRemoved)
                {
                    int currentDepth = currentNode.GetDepth();
                    if (currentDepth > (maxDepth + 1))  
                    {
                        maxDepth = (currentDepth - 1);
                        UpdatePossibleNodesScores(maxDepth);
                        DisplayBestPv(maxDepth);
                    }
                    if (!currentNode.IsCreatedChildren())
                    {
                        return currentNode.CreateChildren(nodeQueue);
                    }
                }
            }
            else
            {
                // No more node to compute => stop engine to save time
                //if(isSmartTiming) this.Stop();
                
            }
            return 0;
        }

        /*
         * Stop the main injector loop
         * 
         */ 
        public void Stop()
        {
            watch.Reset();
            brainStatus = BrainStatus.PAUSED;
        }

        /*
         * Start the main injector loop
         * 
         */ 
        public void Start()
        {
            watch.Start();
            brainStatus = BrainStatus.THINKING;
        }

        public void SetDebug(bool isDebugOn)
        {
            debugMode = isDebugOn;
        }

        public void SetOption(String name, String value)
        {
            switch(name)
            {
                // Simplification => param names without spaces
                case "PawnValue":
                    this.SetPawnValue(Int32.Parse(value));
                    break;
                case "KnightValue":
                    this.SetKnightValue(Int32.Parse(value));
                    break;
                case "BishopValue":
                    this.SetBishopValue(Int32.Parse(value));
                    break;
                case "RookValue":
                    this.SetRookValue(Int32.Parse(value));
                    break;
                case "QueenValue":
                    this.SetRookValue(Int32.Parse(value));
                    break;
                case "Smart Timing":
                    this.SetSmartTiming(value == "on");
                    break;
                default:
                    System.Console.WriteLine("Unknown option: " + name);
                    break;
            }
        }

        private void SetSmartTiming(bool value)
        {
            isSmartTiming = value;
        }


        /**
         * Evaluate the score of the currentPosition used by the Alpha-Beta
         * algorithm
         * 
         * @param currentPosition
         * @return
         */

        public static int Evaluate(Position position)
        {
            int evaluation = 0;
            // TODO improve the evaluation method

            if (IsCheckmate(position)) return (position.GetTurn() ? MIN_SCORE : MAX_SCORE);
            //if(IsStalemate(position)) return 0;

            // Basic evaluation : just count the pieces for ech side with coefs
            int[] pieces = GetPiecesLeft(position);
            for (int i = 0; i < 12; i++)
            {
                evaluation += (pieces[i] * coefficient[i]);
            }


            /*
            for (int i = 0; i < 64; i++)
            {
                Piece p = position.GetPiece(i);
                // Pawn to promotion
                if (p == Piece.WP)
                {
                    int delta = i/8;
                    evaluation += delta;
                }
                else if (p == Piece.BP)
                {
                    int delta = (63-i)/8;
                    evaluation -= delta;
                }
                // Center knights
                else if (p == Piece.WN)
                {
                    int distance = (int)(Math.Pow(3.5f-Square.GetCol(i),2.0) + Math.Pow(3.5f-Square.GetRow(i),2.0f));
                    evaluation += (25-distance);
                }
                else if (p == Piece.BN)
                {
                    int distance = (int)(Math.Pow(3.5f - Square.GetCol(i), 2.0) + Math.Pow(3.5f - Square.GetRow(i), 2.0f));
                    evaluation -= (25 - distance);
                }
            
            } 
             */

            return evaluation;
        }

        /**
         * Get the pieces left on the currentPosition
         * 
         * @param currentPosition
         * @return
         */
        private static int[] GetPiecesLeft(Position position)
        {
            int[] nbpieces = new int[12];
            Piece piece = Piece.OUT_OF_RANGE;
            for (int i = 0; i < 12; i++)
            {
                nbpieces[i] = 0;
            }
            for (int i = 0; i < 64; i++)
            {
                piece = position.GetPiece(i);
                if (piece != Piece.EMPTY)
                {
                    nbpieces[(int)piece]++;
                }
            }

            return nbpieces;
        }

        /**
         * Give the list of possible legal moves
         * @param currentPosition
         * @return
         */
        public static List<Move> GetPossibleMoves(Position position)
        {
            List<Move> moves = new List<Move>();
            //List<Move> pieceMoves = new List<Move>();
            for (int square = 0; square < 64; square++)
            {
                //pieceMoves.Clear();
                if (!position.IsEmptyOrOpponent(square))
                {
                    //pieceMoves = GetPossibleMoves(position, square);
                    //moves.AddRange(pieceMoves);
                    GetPossibleMoves(position, square, moves);
                }
            }
            return moves;
        }

        public static bool IsCheck(Position position)
        {
            return IsCheck(position, position.GetTurn());
        }

        /**
         * Indicate if the currentPosition is check
         * for the given player
         * @param currentPosition
         * @return
         */
        public static bool IsCheck(Position position, bool playerColor)
        {
            //TODO: modify return block to take chessmen power/given check frequency into account
            Piece king = playerColor ? Piece.WK : Piece.BK;
            Piece oKing = playerColor ? Piece.BK : Piece.WK;
            Piece oQueen = playerColor ? Piece.BQ : Piece.WQ;
            Piece oRook = playerColor ? Piece.BR : Piece.WR;
            Piece oBish = playerColor ? Piece.BB : Piece.WB;
            Piece oKnig = playerColor ? Piece.BN : Piece.WN;
            Piece oPawn = playerColor ? Piece.BP : Piece.WP;
            bool oColor = !playerColor;

            // get king square
            int sq;
            for (sq = 0; sq < 64; sq++)
                if (position.GetPiece(sq) == king) break;
            
            int col = Square.GetCol(sq);
            int row = Square.GetRow(sq);


            // king threat (virtual)
            if (col > 0)
                if (position.GetPiece(sq - 1) == oKing) return true;
            if (col > 0 && row < 7)
                if (position.GetPiece(sq + 7) == oKing) return true;
            if (row < 7)
                if (position.GetPiece(sq + 8) == oKing) return true;
            if (col < 7 && row < 7)
                if (position.GetPiece(sq + 9) == oKing) return true;
            if (col < 7)
                if (position.GetPiece(sq + 1) == oKing) return true;
            if (col < 7 && row > 0)
                if (position.GetPiece(sq - 7) == oKing) return true;
            if (row > 0)
                if (position.GetPiece(sq - 8) == oKing) return true;
            if (col > 0 && row > 0)
                if (position.GetPiece(sq - 9) == oKing) return true;

            // Pawn threat
            if (playerColor)
            {
                if (col > 0 && row <7)
                    if (position.GetPiece(sq + 7) == oPawn) return true;
                if (col < 7 && row <7)
                    if (position.GetPiece(sq + 9) == oPawn) return true;
            }
            else
            {
                if (col > 0 && row > 0)
                    if (position.GetPiece(sq - 9) == oPawn) return true;
                if (col < 7 && row > 0)
                    if (position.GetPiece(sq - 7) == oPawn) return true;
            }

            // Knight threat
            if (col > 0 && row < 6 && position.IsEmptyOrColor(sq + 15, oColor)) //optim
                if (position.GetPiece(sq + 15) == oKnig) return true;
            if (col < 7 && row < 6 && position.IsEmptyOrColor(sq + 17, oColor)) //optim
                if (position.GetPiece(sq + 17) == oKnig) return true;
            if (col < 6 && row < 7 && position.IsEmptyOrColor(sq + 10, oColor)) //optim
                if (position.GetPiece(sq + 10) == oKnig) return true;
            if (col < 6 && row > 0 && position.IsEmptyOrColor(sq - 6, oColor)) //optim
                if (position.GetPiece(sq - 6) == oKnig) return true;
            if (col < 7 && row > 1 && position.IsEmptyOrColor(sq - 15, oColor)) //optim
                if (position.GetPiece(sq - 15) == oKnig) return true;
            if (col > 0 && row > 1 && position.IsEmptyOrColor(sq - 17, oColor))//optim
                if (position.GetPiece(sq - 17) == oKnig) return true;
            if (col > 1 && row > 0 && position.IsEmptyOrColor(sq - 10, oColor)) //optim
                if (position.GetPiece(sq - 10) == oKnig) return true;
            if (col > 1 && row < 7 && position.IsEmptyOrColor(sq + 6, oColor))//optim
                if (position.GetPiece(sq + 6) == oKnig) return true;


            // bishop&queen threat
            int i;
            // Up-right moves
            for (i = 1; i < 8; i++)
            {
                if ((col + i) < 8 && (row + i) < 8 && position.IsEmptyOrColor(sq + 9 * i, oColor))//optim
                {
                    Piece p = position.GetPiece(sq + 9 * i);
                    if ((p == oBish) || (p == oQueen)) return true;
                    //else i = 999;
                }
                else
                {
                    i = 999;
                }
            }

            // Down-right moves
            for (i = 1; i < 8; i++)
            {
                if ((col + i) < 8 && (row - i) > -1 && position.IsEmptyOrColor(sq - 7 * i, oColor)) //optimization
                {
                    Piece p = position.GetPiece(sq - 7 * i);
                    if ((p == oBish) || (p == oQueen)) return true;
                    //else i = 999;
                }
                else
                {
                    i = 999;
                }
            }

            // Down-left moves
            for (i = 1; i < 8; i++)
            {
                if ((col - i) > -1 && (row - i) > -1 && position.IsEmptyOrColor(sq - 9 * i, oColor)) //optimization
                {
                    Piece p = position.GetPiece(sq - 9 * i);
                    if ((p == oBish) || (p == oQueen)) return true;
                    //else i = 999;
                }
                else
                {
                    i = 999;
                }
            }
            // Up-left moves
            for (i = 1; i < 8; i++)
            {
                if ((col - i) > -1 && (row + i) < 8 && position.IsEmptyOrColor(sq + 7 * i, oColor)) //optimization
                {
                    Piece p = position.GetPiece(sq + 7 * i);
                    if ((p == oBish) || (p == oQueen)) return true;
                    //else i = 999;
                }
                else
                {
                    i = 999;
                }
            }


            // rook&queen threat
            // Horizontal moves right
            for (i = 1; i < 8; i++)
            {
                if ((col + i) < 8 && position.IsEmptyOrColor(sq + i, oColor)) //optim
                {
                    Piece p = position.GetPiece(sq + i);
                    if ((p == oRook) || (p == oQueen)) return true;
                    //else i = 999;
                }
                else
                {
                    i = 999;
                }
            }

            // Horizontal moves left
            for (i = 1; i < 8; i++)
            {
                if ((col - i) > -1 && position.IsEmptyOrColor(sq - i, oColor)) //optim
                {
                    Piece p = position.GetPiece(sq - i);
                    if ((p == oRook) || (p == oQueen)) return true;
                    //else i = 999;
                }
                else
                {
                    i = 999;
                }
            }

            // Vertical moves up
            for (i = 1; i < 8; i++)
            {
                if ((row + i) < 8 && position.IsEmptyOrColor(sq + i * 8, oColor)) //optim
                {
                    Piece p = position.GetPiece(sq + i * 8);
                    if ((p == oRook) || (p == oQueen)) return true;
                    //else i = 999;
                }
                else
                {
                    i = 999;
                }
            }
            // Vertical moves down
            for (i = 1; i < 8; i++)
            {
                if ((row - i) > -1 && position.IsEmptyOrColor(sq - i * 8, oColor)) //optim
                {
                    Piece p = position.GetPiece(sq - i * 8);
                    if ((p == oRook) || (p == oQueen)) return true;
                    //else i = 999;
                }
                else
                {
                    i = 999;
                }
            }

            return false;





            /*
            List<Move> possibleMoves;
            if (position.GetTurn() != playerColor)
            {
                possibleMoves = GetPossibleMoves(position);
            }
            else
            {
                position.ChangeTurn(); // TODO optimize this part
                possibleMoves = GetPossibleMoves(position);
                position.ChangeTurn(); // TODO optimize this part
            }
            foreach (Move move in possibleMoves)
            {
                if ((position.GetPiece(move.end) == Piece.WK && playerColor) 
                    || (position.GetPiece(move.end) == Piece.BK && !playerColor)) return true; // distinction is important to allow mate
            }
            return false;
             * */
        }

        /**
         * Indicate if the currentPosition is checkmate
         * 
         * @param currentPosition
         * @return
         */
        public static bool IsCheckmate(Position position)
        {
            return (IsCheck(position) && GetLegalMoves(position).Count == 0);
        }

        /**
         * Indicate if the currentPosition is stalemate
         * 
         * @param currentPosition
         * @return
         */
        
        /*
        public static bool IsStalemate(ArrayBoard position)
        {
            // to be called after IsMate
            if (GetPossibleMoves(position).Count == 0) return true;
            //int[] pieces =  GetPiecesLeft(position);
            //return pieces[0] ==0 && 
        }
        */

        /**
         * Get the possible moves for one piece
         * 
         * @param currentPosition
         * @param square
         * @return
         */
        private static void GetPossibleMoves(Position position, int square, List<Move> possibleMoves)
        {
            Piece pieceCode = position.GetPiece(square);

            if ((position.IsBlackToMove() && position.IsPieceBlack(pieceCode))
                    || (position.IsWhiteToMove() && position.IsPieceWhite(pieceCode)))
            {

                switch (pieceCode)
                {
                    case Piece.WP:
                    case Piece.BP:
                        GetPossiblePawnMoves(position, square, possibleMoves);
                        break;
                    case Piece.WB:
                    case Piece.BB:
                        GetPossibleBishopMoves(position, square,possibleMoves);
                        break;
                    case Piece.WN:
                    case Piece.BN:
                        GetPossibleKnightMoves(position, square,possibleMoves);
                        break;
                    case Piece.WR:
                    case Piece.BR:
                        GetPossibleRookMoves(position, square,possibleMoves);
                        break;
                    case Piece.WQ:
                    case Piece.BQ:
                        GetPossibleQueenMoves(position, square,possibleMoves);
                        break;
                    case Piece.WK:
                    case Piece.BK:
                        GetPossibleKingMoves(position, square,possibleMoves);
                        break;
                    default:
                        break;

                }
            }
        }

        /**
         * Get possible moves for King
         * 
         * @param currentPosition
         * @param square
         * @return
         */
        private static void GetPossibleKingMoves(Position position, int square, List<Move> possibleKingMoves)
        {
            //List<Move> possibleKingMoves = new List<Move>(); //optim
            int col = Square.GetCol(square);
            int row = Square.GetRow(square);
            // int piece = currentPosition.getPiece(col, row);

            //if (col < 7 && position.IsEmptyOrOpponent(Square.Get(col + 1, row)))
            if (col < 7 && position.IsEmptyOrOpponent(square+1)) //optim
            {
                //Move move = new Move(square, Square.Get(col + 1, row));
                Move move = new Move(square, square+1); //optim
                possibleKingMoves.Add(move);
            }
            //if (col < 7 && row < 7 && position.IsEmptyOrOpponent(Square.Get(col + 1, row + 1)))
            if (col < 7 && row < 7 && position.IsEmptyOrOpponent(square+9)) //optim
            {
                //Move move = new Move(square, Square.Get(col + 1, row + 1));
                Move move = new Move(square, square+9); //optim
                possibleKingMoves.Add(move);
            }
            //if (row < 7 && position.IsEmptyOrOpponent(Square.Get(col, row + 1)))
            if (row < 7 && position.IsEmptyOrOpponent(square+8))//optim
            {
                //Move move = new Move(square, Square.Get(col, row + 1));
                Move move = new Move(square, square+8); //optim
                possibleKingMoves.Add(move);
            }
            //if (col > 0 && row < 7 && position.IsEmptyOrOpponent(Square.Get(col - 1, row + 1)))
            if (col > 0 && row < 7 && position.IsEmptyOrOpponent(square+7))
            {
                //Move move = new Move(square, Square.Get(col - 1, row + 1));
                Move move = new Move(square, square+7); //optim
                possibleKingMoves.Add(move);
            }
            //if (col > 0 && position.IsEmptyOrOpponent(Square.Get(col - 1, row)))
            if (col > 0 && position.IsEmptyOrOpponent(square-1)) //optim
            {
                //Move move = new Move(square, Square.Get(col - 1, row));
                Move move = new Move(square, square-1); //optim
                possibleKingMoves.Add(move);
            }
            //if (col > 0 && row > 0 && position.IsEmptyOrOpponent(Square.Get(col - 1, row - 1)))
            if (col > 0 && row > 0 && position.IsEmptyOrOpponent(square-9)) //optim
            {
                //Move move = new Move(square, Square.Get(col - 1, row - 1));
                Move move = new Move(square, square-9);
                possibleKingMoves.Add(move);
            }
            //if (row > 0 && position.IsEmptyOrOpponent(Square.Get(col, row - 1)))
            if (row > 0 && position.IsEmptyOrOpponent(square-8)) //optim
            {
                //Move move = new Move(square, Square.Get(col, row - 1));
                Move move = new Move(square, square -8); //optim
                possibleKingMoves.Add(move);
            }
            //if (col < 7 && row > 0 && position.IsEmptyOrOpponent(Square.Get(col + 1, row - 1)))
            if (col < 7 && row > 0 && position.IsEmptyOrOpponent(square-7)) //optim
            {
                //Move move = new Move(square, Square.Get(col + 1,row - 1));
                Move move = new Move(square, square-7);
                possibleKingMoves.Add(move);
            }

            // TODO implement possible Casteling moves
            if (position.GetTurn())
            {
                if (position.whiteCanCastleQueenSide 
                    && position.GetPiece(Square.B1) == Piece.EMPTY
                    && position.GetPiece(Square.C1) == Piece.EMPTY
                    && position.GetPiece(Square.D1) == Piece.EMPTY
                    )
                {
                    Move subMove = new Move(Square.E1, Square.D1);
                    Position posTest = new Position(position);
                    posTest.Play(subMove);
                    if (!IsCheck(posTest))
                    {
                        possibleKingMoves.Add(new Move(Square.E1, Square.C1));
                    }
                }
                if (position.whiteCanCastleKingSide
                        && position.GetPiece(Square.E1) == Piece.EMPTY
                        && position.GetPiece(Square.F1) == Piece.EMPTY)
                {
                    Move subMove = new Move(Square.E1, Square.F1);
                    Position posTest = new Position(position);
                    posTest.Play(subMove);
                    if (!IsCheck(posTest))
                    {
                        possibleKingMoves.Add(new Move(Square.E1, Square.G1));
                    }
                }
            } else
            {
                if (position.blackCanCastleQueenSide 
                    && position.GetPiece(Square.B8) == Piece.EMPTY
                    && position.GetPiece(Square.C8) == Piece.EMPTY
                    && position.GetPiece(Square.D8) == Piece.EMPTY
                    )
                {
                    Move subMove = new Move(Square.E8, Square.D8);
                    Position posTest = new Position(position);
                    posTest.Play(subMove);
                    if (!IsCheck(posTest))
                    {
                        possibleKingMoves.Add(new Move(Square.E8, Square.C8));
                    }
                }
                if (position.blackCanCastleKingSide
                        && position.GetPiece(Square.E8) == Piece.EMPTY
                        && position.GetPiece(Square.F8) == Piece.EMPTY)
                {
                    Move subMove = new Move(Square.E8, Square.F8);
                    Position posTest = new Position(position);
                    posTest.Play(subMove);
                    if (!IsCheck(posTest))
                    {
                        possibleKingMoves.Add(new Move(Square.E8, Square.G8));
                    }
                }
            }
            





            //return possibleKingMoves; //optim
        }

        /**
         * Get possible moves for Queen
         * 
         * @param currentPosition
         * @param square
         * @return
         */
        private static void GetPossibleQueenMoves(Position position, int square, List<Move> possibleQueenMoves)
        {
            //List<Move> possibleQueenMoves = new List<Move>(); optim
            GetPossibleRookMoves(position, square, possibleQueenMoves);
            GetPossibleBishopMoves(position, square, possibleQueenMoves);
            //return possibleQueenMoves;//optim
        }

        /*
         * Get possible moves for Rook
         */
        private static void GetPossibleRookMoves(Position position, int square, List<Move> possibleRookMoves)
        {
            //List<Move> possibleRookMoves = new List<Move>();//optim
            int col = Square.GetCol(square);
            int row = Square.GetRow(square);

            // Horizontal moves right
            for (int i = 1; i < 8; i++)
            {
                //if ((col + i) < 8 && position.IsEmptyOrOpponent(Square.Get(col + i, row)))
                if ((col + i) < 8 && position.IsEmptyOrOpponent(square + i)) //optim
                {
                    //Move move = new Move(square, Square.Get(col + i, row));
                    Move move = new Move(square, square + i);
                    possibleRookMoves.Add(move);
                    //if (!position.IsSquareEmpty(Square.Get(col + i, row)))
                    if (!position.IsSquareEmpty(square + i)) //optim
                    {
                        i = 999;
                    }
                }
                else
                {
                    i = 999;
                }
            }

            // Horizontal moves left
            for (int i = 1; i < 8; i++)
            {
                //if ((col - i) > -1 && position.IsEmptyOrOpponent(Square.Get(col - i, row)))
                if ((col - i) > -1 && position.IsEmptyOrOpponent(square - i)) //optim
                {
                    //Move move = new Move(square, Square.Get(col - i, row));
                    Move move = new Move(square, square - i); //optim
                    possibleRookMoves.Add(move);
                    //if (!position.IsSquareEmpty(Square.Get(col - i, row)))
                    if (!position.IsSquareEmpty(square - i)) //optim
                    {
                        i = 999;
                    }
                }
                else
                {
                    i = 999;
                }
            }

            // Vertical moves up
            for (int i = 1; i < 8; i++)
            {
                //if ((row + i) < 8 && position.IsEmptyOrOpponent(Square.Get(col, row + i)))
                if ((row + i) < 8 && position.IsEmptyOrOpponent(square + i*8)) //optim
                {
                    //Move move = new Move(square, Square.Get(col, row + i));
                    Move move = new Move(square, square + i*8); //optim
                    possibleRookMoves.Add(move);
                    //if (!position.IsSquareEmpty(Square.Get(col, row + i)))
                    if (!position.IsSquareEmpty(square + i * 8)) //optim
                    {
                        i = 999;
                    }
                }
                else
                {
                    i = 999;
                }
            }
            // Vertical moves down
            for (int i = 1; i < 8; i++)
            {
                //if ((row - i) > -1 && position.IsEmptyOrOpponent(Square.Get(col, row - i)))
                if ((row - i) > -1 && position.IsEmptyOrOpponent(square - i*8)) //optim
                {
                    //Move move = new Move(square, Square.Get(col, row - i));
                    Move move = new Move(square, square - i * 8); //optim
                    possibleRookMoves.Add(move);
                    //if (!position.IsSquareEmpty(Square.Get(col, row - i)))
                    if (!position.IsSquareEmpty(square - i * 8)) //optim
                    {
                        i = 999;
                    }
                }
                else
                {
                    i = 999;
                }
            }

            //return possibleRookMoves;//optim
        }

        /**
         * Get possible moves for Knight
         * 
         * @param currentPosition
         * @param square
         * @return
         */
        private static void GetPossibleKnightMoves(Position position, int square, List<Move> possibleKnightMoves)
        {
            //List<Move> possibleKnightMoves = new List<Move>(); //optim
            int col = Square.GetCol(square);
            int row = Square.GetRow(square);

            //if (col > 0 && row < 6 && position.IsEmptyOrOpponent(Square.Get(col - 1, row + 2)))
            if (col > 0 && row < 6 && position.IsEmptyOrOpponent(square+15)) //optim
            {
                //Move move = new Move(square, Square.Get(col - 1, row + 2));
                Move move = new Move(square, square+15);
                possibleKnightMoves.Add(move);
            }
            //if (col < 7 && row < 6 && position.IsEmptyOrOpponent(Square.Get(col + 1, row + 2)))
            if (col < 7 && row < 6 && position.IsEmptyOrOpponent(square+17)) //optim
            {
                //Move move = new Move(square, Square.Get(col + 1, row + 2));
                Move move = new Move(square, square+17); //optim
                possibleKnightMoves.Add(move);
            }
            //if (col < 6 && row < 7 && position.IsEmptyOrOpponent(Square.Get(col + 2, row + 1)))
            if (col < 6 && row < 7 && position.IsEmptyOrOpponent(square+10)) //optim
            {
                //Move move = new Move(square, Square.Get(col + 2, row + 1));
                Move move = new Move(square, square+10); //optim
                possibleKnightMoves.Add(move);
            }
            //if (col < 6 && row > 0 && position.IsEmptyOrOpponent(Square.Get(col + 2, row - 1)))
            if (col < 6 && row > 0 && position.IsEmptyOrOpponent(square-6)) //optim
            {
                //Move move = new Move(square, Square.Get(col + 2, row - 1));
                Move move = new Move(square, square-6); //optim
                possibleKnightMoves.Add(move);
            }
            //if (col < 7 && row > 1 && position.IsEmptyOrOpponent(Square.Get(col + 1, row - 2)))
            if (col < 7 && row > 1 && position.IsEmptyOrOpponent(square-15)) //optim
            {
                //Move move = new Move(square, Square.Get(col + 1, row - 2));
                Move move = new Move(square, square-15);//optim
                possibleKnightMoves.Add(move);
            }
            //if (col > 0 && row > 1 && position.IsEmptyOrOpponent(Square.Get(col - 1, row - 2)))
            if (col > 0 && row > 1 && position.IsEmptyOrOpponent(square - 17))//optim
            {
                //Move move = new Move(square, Square.Get(col - 1, row - 2));
                Move move = new Move(square, square-17); //optim
                possibleKnightMoves.Add(move);
            }
            //if (col > 1 && row > 0 && position.IsEmptyOrOpponent(Square.Get(col - 2, row - 1)))
            if (col > 1 && row > 0  && position.IsEmptyOrOpponent(square-10)) //optim
            {
                //Move move = new Move(square, Square.Get(col - 2, row - 1));
                Move move = new Move(square, square-10); //optim
                possibleKnightMoves.Add(move);
            }
            //if (col > 1 && row < 7 && position.IsEmptyOrOpponent(Square.Get(col - 2, row + 1)))
            if (col > 1 && row < 7 && position.IsEmptyOrOpponent(square+6))//optim
            {
                //Move move = new Move(square, Square.Get(col - 2, row + 1));
                Move move = new Move(square, square+6);//optim
                possibleKnightMoves.Add(move);
            }

            //return possibleKnightMoves;//optim
        }

        /**
         * Get possible moves for Bishop
         * 
         * @param currentPosition
         * @param square
         * @return
         */
        private static void GetPossibleBishopMoves(Position position, int square, List<Move> possibleBishopMoves)
        {
            //List<Move> possibleBishopMoves = new List<Move>(); //optim
            int col = Square.GetCol(square);
            int row = Square.GetRow(square);
            int i;

            // Up-right moves
            for (i = 1; i < 8; i++)
            {
                if ((col + i) < 8 && (row + i) < 8 && position.IsEmptyOrOpponent(square + 9*i))//optim
                {
                    Move move = new Move(square, square + i * 9); //optimization
                    possibleBishopMoves.Add(move);
                    if (!position.IsSquareEmpty(square + i * 9)) //optimization
                    {
                        i = 999;
                    }
                }
                else
                {
                    i = 999;
                }
            }

            // Down-right moves
            for (i = 1; i < 8; i++)
            {
                if ((col + i) < 8 && (row - i) > -1 && position.IsEmptyOrOpponent(square-7*i)) //optimization
                {
                    Move move = new Move(square, square-7*i); //optimization
                    possibleBishopMoves.Add(move);
                    if (!position.IsSquareEmpty(square - 7*i)) //optimization
                    {
                        i = 999;
                    }
                }
                else
                {
                    i = 999;
                }
            }

            // Down-left moves
            for (i = 1; i < 8; i++)
            {
                if ((col - i) > -1 && (row - i) > -1 && position.IsEmptyOrOpponent(square - 9*i)) //optimization
                {
                    Move move = new Move(square, square - 9*i); //optimization
                    possibleBishopMoves.Add(move);
                    if (!position.IsSquareEmpty(square - 9*i)) //optimization
                    {
                        i = 999;
                    }
                }
                else
                {
                    i = 999;
                }
            }
            // Up-left moves
            for (i = 1; i < 8; i++)
            {
                if ((col - i) > -1 && (row + i) < 8 && position.IsEmptyOrOpponent(square+7*i)) //optimization
                {
                    Move move = new Move(square, square+7*i); //optimization
                    possibleBishopMoves.Add(move);
                    if (!position.IsSquareEmpty(square+7*i)) //optimization
                    {
                        i = 999;
                    }
                }
                else
                {
                    i = 999;
                }
            }
        }

        /**
         * Get possible moves for Pawn
         * 
         * @param currentPosition
         * @param square
         * @return
         */
        private static void GetPossiblePawnMoves(Position position, int square, List<Move> possiblePawnMoves)
        {
            List<Move> pawnMoves = new List<Move>();//optim
            Piece piece = position.GetPiece(square);
            int col = Square.GetCol(square);
            int row = Square.GetRow(square);
            
            if (piece == Piece.WP && row < 7)
            {
                // Pawn's move
                //if (position.GetPiece(Square.Get(col, row + 1)) == Piece.EMPTY)
                if (position.GetPiece(square+8) == Piece.EMPTY) //optim
                {
                    //Move move = new Move(square, Square.Get(col, row + 1));
                    Move move = new Move(square, square+8); //optim
                    pawnMoves.Add(move);
                }
                //if (row == 1 && (position.GetPiece(Square.Get(col, row + 1)) == Piece.EMPTY) && (position.GetPiece(Square.Get(col, row + 2)) == Piece.EMPTY))
                if (row == 1 && (position.GetPiece(square+8) == Piece.EMPTY) && (position.GetPiece(square+16) == Piece.EMPTY)) //optim
                {
                    //Move move = new Move(square, Square.Get(col, row + 2));
                    Move move = new Move(square, square+16);
                    pawnMoves.Add(move);
                }
                // Pawn capture
                //if (col < 7 && position.IsPieceBlack(position.GetPiece(Square.Get(col + 1, row + 1))))
                if (col < 7 && (position.IsPieceBlack(position.GetPiece(square+9)) || (position.lastEnPassant == (square+9)))) //optim
                {
                    //Move move = new Move(square, Square.Get(col + 1, row + 1));
                    Move move = new Move(square, square+9); //optim
                    pawnMoves.Add(move);
                }
                //if (col > 0 && position.IsPieceBlack(position.GetPiece(Square.Get(col - 1, row + 1))))
                if (col > 0 && (position.IsPieceBlack(position.GetPiece(square+7))|| (position.lastEnPassant == (square+7)))) //optim
                {
                    //Move move = new Move(square, Square.Get(col - 1, row + 1));
                    Move move = new Move(square, square+7); //optim
                    pawnMoves.Add(move);
                }
                // TODO implement "en passant"

            }
            if (piece == Piece.BP && row > 0)
            {
                // Pawn's move
                //if (position.GetPiece(Square.Get(col, row - 1)) == Piece.EMPTY)
                if (position.GetPiece(square-8) == Piece.EMPTY)//optim
                {
                    //Move move = new Move(square, Square.Get(col, row - 1));
                    Move move = new Move(square, square-8); //optim
                    pawnMoves.Add(move);
                }
                //if (row == 6 && (position.GetPiece(Square.Get(col, row - 1)) == Piece.EMPTY) && (position.GetPiece(Square.Get(col, row - 2)) == Piece.EMPTY))
                if (row == 6 && (position.GetPiece(square-8) == Piece.EMPTY) && (position.GetPiece(square-16) == Piece.EMPTY)) //optim
                   
                {
                    //Move move = new Move(square, Square.Get(col, row - 2));
                    Move move = new Move(square, square-16);//optim
                    pawnMoves.Add(move);

                }
                // Pawn capture
                //if (col < 7 && position.IsPieceWhite(position.GetPiece(Square.Get(col + 1, row - 1))))
                if (col < 7 && (position.IsPieceWhite(position.GetPiece(square-7))|| (position.lastEnPassant == (square-7))))//optim
                {
                    //Move move = new Move(square, Square.Get(col + 1, row - 1));
                    Move move = new Move(square, square-7);
                    pawnMoves.Add(move);
                }
                //if (col > 0 && position.IsPieceWhite(position.GetPiece(Square.Get(col - 1, row - 1))))
                if (col > 0 && (position.IsPieceWhite(position.GetPiece(square - 9)) || (position.lastEnPassant == (square - 9))))//optim
                {
                    //Move move = new Move(square, Square.Get(col - 1, row - 1));
                    Move move = new Move(square, square-9);//optim
                    pawnMoves.Add(move);
                }
                // TODO implement "en passant"
            }

            // adding alternate promotions
            foreach (Move m in pawnMoves)
            {
                int r = Square.GetRow(m.end);
                if (m.promotion == '0' && (r == 0 || r == 7))
                {
                    possiblePawnMoves.Add(new Move(m.begin, m.end, 'q'));
                    possiblePawnMoves.Add(new Move(m.begin, m.end, 'r'));
                    possiblePawnMoves.Add(new Move(m.begin, m.end, 'b'));
                    possiblePawnMoves.Add(new Move(m.begin, m.end, 'n'));
                }
                else
                {
                    possiblePawnMoves.Add(m);
                }
            }
            
            // adding en passant moves (no possible promotion with en-passant)
            if(position.lastEnPassant != 65)
            {
                int target = position.lastEnPassant;
                if (position.GetTurn())
                {
                    if (Square.GetCol(target) > 0 && position.GetPiece(target - 1) == Piece.WP)
                    {
                        possiblePawnMoves.Add(new Move(target - 1, target + 8));
                    }
                    if (Square.GetCol(target) < 7 && position.GetPiece(target + 1) == Piece.WP)
                    {
                        possiblePawnMoves.Add(new Move(target + 1, target + 8));
                    }
                } else
                {
                    if (Square.GetCol(target) > 0 && position.GetPiece(target - 1) == Piece.BP)
                    {
                        possiblePawnMoves.Add(new Move(target - 1, target - 8));
                    }
                    if (Square.GetCol(target) < 7 && position.GetPiece(target + 1) == Piece.BP)
                    {
                        possiblePawnMoves.Add(new Move(target + 1, target - 8 ));
                    }
                }
            } 
            
            
            
            //return possiblePawnMoves;//optim
        }


        /**
         * Returns true is the move from c1,r1 to c2,r2 is possible
         * 
         * @param currentPosition
         * @param move
         * @return
         */
        /*
        public bool IsPossibleMove(ArrayBoard position, Move move)
        {
            List<Move> possibleMoves = GetPossibleMoves(position, move.begin);
            for (int i = 0; i < possibleMoves.Count; i++)
            {
                if (possibleMoves[i].IsEqual(move))
                {
                    return true;
                }
            }
            return false;
        }
         * */

        /**
         * 
         * @param currentPosition
         * @param move
         * @return
         */
        /*
         * private bool IsKingExposed(ArrayBoard position, Move move)
        {
            Position testBoard = new Position(position);
            int currentTurn = position.GetTurn();
            testBoard.Play(move);
            return IsCheck(testBoard, currentTurn);
        }
         * */

        /**
         * Get legal moves
         * @param currentPosition
         * @return
         */
        public static List<Move> GetLegalMoves(Position pos)
        {
            List<Move> possibleMoves = GetPossibleMoves(pos);
            List<Move> legalMoves = new List<Move>();
            foreach (Move m in possibleMoves)
            {
                Position newPosition = new Position(pos);
                newPosition.Play(m);
                if (!IsCheck(newPosition,!newPosition.GetTurn()))
                {
                    legalMoves.Add(m);
                }
            }
            return legalMoves;
        }

 
        public Move GetBestMove()
        {
            /*if (isVerbose || debugMode)
            {
                System.Console.WriteLine("###########################################");
                foreach (ArrayBoardNode n in possibleNodes)
                {
                    System.Console.WriteLine(n.GetPreviousMove().ToString() + " score=" + n.GetScore());
                }
                System.Console.WriteLine("###########################################");
            }*/
            if (possibleNodes.Count > 0)
            {
                return GetBestNode().GetPreviousMove();                
            }
            return Move.Null;
        }

        private Node GetBestNode()
        {
            if (this.currentPosition.GetTurn())
            {
                return GetMaxNode(possibleNodes);
            }
            else
            {
                return GetMinNode(possibleNodes);
            }
        }

        public static Node GetMinNode(List<Node> possibleNodes)
        {
            int minScore = 99999;
            Node minNode = null;
            foreach (Node node in possibleNodes)
            {
                int score = node.GetScore();
                if (score < minScore)
                {
                    minScore = score;
                    minNode = node;
                }
            }
            return minNode;
        }

        public static Node GetMaxNode(List<Node> possibleNodes)
        {
            int maxScore = -99999;
            Node maxNode = null;
            foreach (Node node in possibleNodes)
            {
                int score = node.GetScore();
                if (score > maxScore)
                {
                    maxScore = score;
                    maxNode = node;
                }
            }
            return maxNode;
        }

        void InitNodeQueue(List<Move> moves)
        {
            nodeQueue.Clear();
            maxDepth = 0;
            possibleNodes.Clear();
            //possibleMoves.Clear();
            // adding lvl n+1 root nodes
            foreach (Move move in moves)
            {
                Position childPosition = new Position(currentPosition);
                childPosition.Play(move);
                Node child = new Node(move, childPosition, 1);
                nodeQueue.Enqueue(child);
                possibleNodes.Add(child);
            }
            //UpdatePossibleNodesScores(maxDepth);
            //DisplayBestPv(maxDepth);
        }



        public void SetDepthLevel(int depth)
        {
            this.depthLevel = depth;
        }

        public void SetTimeLevel(int timeLevel)
        {
            brainMode = BrainMode.MOVETIME_MODE;
            this.thinkDelay = timeLevel;
        }

        public void SetDepthThinkMode()
        {
            brainMode = BrainMode.DEPTH_MODE;
        }

        public void SetTimeThinkMode()
        {
            brainMode = BrainMode.MOVETIME_MODE;
        }


        public void SetStartPosition()
        {
            SetPositionFromFEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", null);
        }


        public void SetPositionFromFEN(String fen, List<Move> moves)
        {
            // TODO check this part
            this.nodeQueue.Clear();
            this.possibleNodes.Clear();
            //this.possibleMoves.Clear();
            //this.hashtable.Clear();
            /////////////

            this.currentPosition.SetFromFEN(fen);
            if (moves != null)
            {
                foreach (Move move in moves)
                {
                    currentPosition.Play(move);
                }
            }
            InitNodeQueue(GetLegalMoves(currentPosition));
        }

        public void Ponderhit()
        {
            throw new NotImplementedException();
        }

        public void SetSearchmovesMode(List<Move> moves)
        {
            nodeQueue.Clear();
            // don't forget to switch to false once used 
            //IsSearchmoveMode = true;
            InitNodeQueue(moves);
        }

        public void SetPonderMode()
        {
            throw new NotImplementedException();
        }

        public void SetWtime(int timeLeft)
        {
            this.wtime = new TimeSpan(0, 0, 0, 0, timeLeft);
            this.brainMode = BrainMode.ADJUSTTIME_MODE;
        }

        public void SetBtime(int timeLeft)
        {
            this.btime = new TimeSpan(0, 0, 0, 0, timeLeft);
            this.brainMode = BrainMode.ADJUSTTIME_MODE;
        }

        public void SetWinc(int increment)
        {
            this.winc = new TimeSpan(0,0,0,0,increment);
            this.brainMode = BrainMode.ADJUSTTIME_MODE;
        }

        public void SetBinc(int increment)
        {
            this.binc = new TimeSpan(0, 0, 0, 0, increment);
            this.brainMode = BrainMode.ADJUSTTIME_MODE;
        }

        public void SetMovestogo(int movesUntilNextTimeControl)
        {
            throw new NotImplementedException();
        }

        public void SetDepthMode(int pliesDepth)
        {
            brainMode = BrainMode.DEPTH_MODE;
            depthLevel = pliesDepth; 
        }

        public void SetNodesMode(int nodesDepth)
        {
            brainMode = BrainMode.NODES_MODE;
            this.nodeMaxNumber = nodesDepth;
        }

        public void SetMateMode(int mateDepth)
        {
            brainMode = BrainMode.MATE_MODE;
            //brainMode = brainMode.DEPTH_MODE;
            depthLevel = mateDepth;
        }

        public void SetMovetimeMode(int searchTime)
        {
            this.thinkDelay = searchTime;
            brainMode = BrainMode.MOVETIME_MODE;
        }

        public void SetInfiniteMode()
        {
            brainMode = BrainMode.INFINITE_MODE;
        }

        public void SetVerboseMode(bool isVerboseMode)
        {
            isVerbose = isVerboseMode;
        }

        private void UpdateThinkDelay()
        {
            int playerInc = 0;
            int playerTime = 0;
            int oppInc = 0;
            int oppTime = 0;
            if(currentPosition.GetTurn())
            {
                playerInc = (int)winc.TotalMilliseconds;
                playerTime = (int)wtime.TotalMilliseconds;
                oppInc = (int)binc.TotalMilliseconds;
                oppTime = (int)btime.TotalMilliseconds;
            } else {
                playerInc = (int)this.binc.TotalMilliseconds;
                playerTime = (int)btime.TotalMilliseconds;
                oppInc = (int)winc.TotalMilliseconds;
                oppTime = (int)wtime.TotalMilliseconds;
            }
            int delay;

            // Smart timing : the engine adjust its think time depending on 
            // the time left for itself and the opponent
            // TODO: improve calculation
            // TODO: link calculation to current nodeStack/score state
            if (isSmartTiming)
            {
                if (playerTime > oppTime)
                {
                    delay = (int)Math.Max(playerInc, playerTime / 20);
                }
                else
                {
                    delay = (int)Math.Max(playerInc, playerTime / 20);
                }
            } else
            {
                delay = (int)Math.Max(playerInc, playerTime / 20);
            }


            thinkDelay = delay;
            System.Console.WriteLine("Think delay updated : " + thinkDelay );
        }
/*
        public void SortLegalMoves()
        {
            possibleNodes.Sort();
        }
        */
        public void UpdatePossibleNodesScores(int maxDepth) {
            lock (possibleNodes)
            {/*
                foreach (ArrayBoardNode node in possibleNodes)
                {
                    node.UpdateScore(maxDepth, currentPosition.GetTurn());
                }*/
                for (int i = 0; i < possibleNodes.Count; i++) possibleNodes[i].UpdateScore(maxDepth, currentPosition.GetTurn());
                if (isVerbose)
                {
                    //possibleNodes.Sort();
                    System.Console.WriteLine("END =================================== DEBUG"); if (isVerbose) System.Console.WriteLine("Node scores update ==================== DEBUG");
                    for (int i = 0; i < possibleNodes.Count;i++ )
                    {
                        Node node = possibleNodes[i];
                        System.Console.WriteLine(node.GetPreviousMove().ToString() + " score=" + node.GetScore());
                    }
                    System.Console.WriteLine("END =================================== DEBUG");
                }
                 
            }
        
        }

        public void DisplayBestPv(int maxDepth) {

            if (possibleNodes.Count > 0) {
                //SortLegalMoves();

                int depth = 1;
                Node bestNode = GetBestNode();
                Console.WriteLine("info currmove " + bestNode.GetPreviousMove());

                long time = watch.ElapsedMilliseconds;
                int nodeNb = nodeQueue.Count;//+hashtable.Count;
                long nps = (time == 0) ? 0 : (1000 * nodeNb / time);
                //int hashfull = (hashMaxSize == 0) ? 0 : (1000 * hashtable.Count / hashMaxSize);
                StringBuilder pv = new StringBuilder("info nodes " + nodeNb + " nps " + nps +/* " hashfull " + hashfull+*/  " depth " + maxDepth + " score cp " + bestNode.GetScore() + " time " + watch.ElapsedMilliseconds + " pv");
                while ((depth < maxDepth) && (!bestNode.IsLeave()) )
                {
                    depth++;
                    pv.Append(" " + bestNode.GetPreviousMove().ToString());
                    if (currentPosition.GetTurn())
                    {
                        bestNode = GetMaxNode(bestNode.Children);
                    }
                    else
                    {
                        bestNode = GetMinNode(bestNode.Children);
                    }
                }
                pv.Append(" " + bestNode.GetPreviousMove().ToString());

                System.Console.WriteLine(pv);
            }
        }
    }
}